/*
 * ProteinBoxBot.java
 *
 * Created on May 16, 2007, 3:37 PM
 *
 * Protein Box Bot (PBB) is a program to automatically update wikipedia info for gene
 * data.  Specifically it shall add information into a protein box template (PBT).
 *
 * PBB has three main aspects of function.  It must first read in XML datafiles
 * that contain gene information to be added into the PBT.  The bot must also retrieve
 * the current wiki page for the gene (if any) and look for any flags that indicate
 * it should not longer update the page, and also the location in the page that
 * should be updated while preserving all other information.  It then must create
 * a new wiki page based off of the old page and the updated information.  This new page
 * Must then be uploaded to the wiki server.  The bot must do extensive logging of the
 * Changes made and must be very careful to not delete information.
 *
 * Wiki communication specs:
 * The PBB must log in to wikipedia as ProteinBoxBot
 *
 * Uploading Specs:
 * The bot may not make more than 10 updates to the wiki server per minute (or 1 per 6 seconds)
 * A timer must be implimented, along with an update queue.  The timer will indicate when an update
 * may occur, the next queued update will happen, and then the timer will reset.
 * The updates will be handled by a seperate thread so that other processing may occur
 * while the system is waiting to update.
 *
 * Copyright 2007-2008 Jon W Huss III
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
 */
package proteinboxbot;

//Window Imports
//import com.sun.org.apache.xalan.internal.xsltc.compiler.Template;
import javax.swing.*;
import java.awt.event.*;
import proteinboxbot.*;
import java.net.*;
import java.io.*;
import java.net.CookieManager;
import java.util.*;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.util.regex.*;
import org.apache.commons.lang.StringEscapeUtils;

/**
 *
 * @author Jon Huss
 */
public class ProteinBoxBot {

    //Useful for Debugging...
    //public static final String pageBase = "User:JonSDSUGrad/Sandbox/Demo_";
    //public static final String pageBase = "";
    private String pageBase;
    public static final boolean debugUpload = false;
    public static final boolean EXPERIMENT = true;
    private String matchGridPageData = "";
    private String lineSep = null;
    //Global Variables:
    //flags for program operation:
    boolean glblUseGui;
    String glblConfigFile = null;
    PBBconfig glblConfiguration = null;
    boolean glblParseGoTerms;
    PBBLogging glblBotLog = null;
    PBBFileIO glblFileIO = null;
    Hashtable<String, String> glblPageLocations;
    Hashtable<String, String> glblPageTitles;
    PBBWikiCom glblWikiCom = null;
    PBBxml glblXmlParser = null;
    PBBCitation glblCitation = null;
    //Means this protein had an error while processing and was skipped
    //helps us create a list of skipped proteins for later processing.
    Hashtable<String, Boolean> glblProteinErrors;
    String glblRunLogPage; //based on glblConfiguration.getWikiLogRun();

    /** Creates a new instance of ProteinBoxBot */
    public ProteinBoxBot(boolean ugui, String ConfigFileLocation, boolean pgt) {

        glblParseGoTerms = pgt;
        glblUseGui = ugui;
        glblConfigFile = ConfigFileLocation;
        lineSep = System.getProperty("line.separator");
        glblBotLog = null;
        glblPageLocations = new Hashtable<String, String>();
        glblPageTitles = new Hashtable<String, String>();
        glblProteinErrors = new Hashtable<String, Boolean>();

        //First check to see if the configuration file can be parsed.
        try {
            glblConfiguration = new PBBconfig();
            glblBotLog = glblConfiguration.init(glblConfigFile);

        } catch (Exception e) {
            System.out.println("Error while Parsing Config File:");
            System.out.println(e.getMessage());
            e.printStackTrace();
            this.cleanUp();
            //e.printStackTrace();
            System.exit(1);
        }


        //Decide if we want to run test code or do normal processing..
        boolean normalProcessing = true;
        
        if (!normalProcessing) {
        //Test Code:
            


        //****Normal Processing****
        } else {
            glblFileIO = new PBBFileIO(glblBotLog);
            //Ok, so we are going to get a list of geneIds from the input file.
            //we then download the XML data and store it into a file.
            //that file is then parsed appropriately.

            String[] geneIds = glblFileIO.getFileLines(glblConfiguration.getXmlIn());


            if (geneIds == null) {
                //didn't get any input for some reason..
                System.out.println("Error while reading geneId List.  Aborting operation.");
                this.cleanUp();
                System.out.flush();
                System.exit(0); //A bit sloppy
            }

            //*** new Code ****
            String input;
            BufferedReader inputLine;
            //Make these global questions at the start.
            try {
                inputLine = new BufferedReader(new InputStreamReader(System.in));
                System.out.println("Communicate data to Wiki? (y/n)");
                input = inputLine.readLine();
                //input = "y";
                if (input.compareToIgnoreCase("y") == 0) {
                    uploadToWiki = true;
                }
                if (uploadToWiki) {
                    System.out.println("Perform a Dry run only? (y/n)");
                    input = inputLine.readLine();
                    //input = "y";
                    if (input.compareToIgnoreCase("n") == 0) {
                        dryRunOnly = false;
                    }
                }
            } catch (IOException ex) {
                System.out.println("Unrecoverable Error. aborting bot.");
                this.cleanUp();
                ex.printStackTrace();
                System.exit(1);
            }
            Hashtable<String, String> gts = this.retrieveGoTerms();
            //  else { // instead of for
            int batchSize = 25;
            int numBatch = (int) Math.ceil((double) geneIds.length / (double) batchSize);
            for (int i = 0; i < numBatch; i++) {

                //****DEBUGGING VARIABLE****
                pageBase = this.glblConfiguration.getPageBase();

                System.out.println("*****Starting Batch job " + (i + 1) + " of " + numBatch + " *******");
                int valStart = i * batchSize;
                int valLength;
                if (valStart + batchSize - 1 < geneIds.length) {
                    valLength = batchSize;
                } else {
                    valLength = geneIds.length - valStart;
                }
                //valstart and end should now point to the 25 range of ids...

                String[] geneIdsBatch = new String[valLength];
                System.out.println("*****Batch Size: " + valLength);
                System.arraycopy(geneIds, valStart, geneIdsBatch, 0, valLength);

                //also modify the log page to be unique.
                glblRunLogPage = glblConfiguration.getWikiLogRun() + "-" + i;

                PBBxmlGetter xmlGetter = new PBBxmlGetter(glblConfiguration.getDataOut());
                String xmlFile = xmlGetter.getXmlData(geneIdsBatch);

                glblXmlParser = new PBBxml(glblConfiguration.getDataOut(), xmlFile, glblBotLog);
                glblCitation = new PBBCitation(glblBotLog, glblXmlParser);
                String[] simpleFiles = null;
                try {
                    simpleFiles = glblXmlParser.xmlDomParse(glblConfiguration.getXmlDecoder(glblConfiguration.getXmlSeek()));
                } catch (Exception ex) {
                    System.out.println("Error while Parsing XML.  Closing Log Files.");
                    this.cleanUp();
                    ex.printStackTrace();
                    System.exit(1);
                }

                PBBdata tempData; // = new PBBdata();
                if (simpleFiles == null) {
                    System.out.println("Error: No Proteins Parsed from XML file.");
                    glblBotLog.addEntry("SYSTEM", "XML ERROR", "No Proteins Parsed from XML file.", PBBLogging.logs.SY);
                    break;
                }
                if (simpleFiles.length < 1) {
                    System.out.println("Error: No Proteins Parsed from XML file.");
                    glblBotLog.addEntry("SYSTEM", "XML ERROR", "No Proteins Parsed from XML file.", PBBLogging.logs.SY);
                    break;
                }
                System.out.println("Finished Building Simple Format Files. (" + simpleFiles.length + " Files Total)");
                glblBotLog.addEntry("SYSTEM", "XML", "Finished building Simple Format Files. (" + simpleFiles.length + " Files Total)", PBBLogging.logs.SY);

                //Start web Parsing...
                //Parsed all the data in Simple Format Files.
                this.WikiProcessing(simpleFiles, gts);

                //Ok, this is kind of sad that we have to reinstantiate all of this stuff after every run, but
                //such is coding.  All objects should be re-instantiated between every batch.
                this.cleanUp();


                try {

                    glblConfiguration = new PBBconfig();
                    glblBotLog = glblConfiguration.init(glblConfigFile);

                } catch (Exception e) {
                    System.out.println("Error while Parsing Config File:");
                    System.out.println(e.getMessage());
                    e.printStackTrace();
                    this.cleanUp();
                    //e.printStackTrace();
                    System.exit(1);
                }
                glblFileIO = new PBBFileIO(glblBotLog);

            }

        //Output the Log files..
        //this.cleanUp();

        //} catch (Exception e) {
        //    e.printStackTrace();
        //}

        }
        System.out.println("PBB Run Completed.");
        System.out.flush();
        System.exit(0); //A bit sloppy


    }

    private void cleanUp() {
        //Procedure that should be called before shutdown of the bot
        if (glblBotLog != null) {
            glblBotLog.outputLogs(glblPageLocations);
            try {
                glblBotLog.closeLogs();
            } catch (IOException ex) {
                System.out.println("Error while Closing Log Files.");
                ex.printStackTrace();
            }

        }
    }
    //Now global
    private boolean uploadToWiki = false;
    private boolean dryRunOnly = true;

    private void WikiProcessing(String[] fileList, Hashtable<String, String> gts) {
        //gts is the GO Terms hashtable.



        PBBWikiCoder myCoder;
        PBBdata data;





        // Page directory - only used if uploadToWiki is true.
        Hashtable<String, String> pageDir = null;



        //Basic flow chart for each file:
        // Parse Simple File
        //download Existing Page
        //
        //boolean uploadToWiki = false;
        //boolean dryRunOnly = true;

        //These questions have hence been made global and asked before the batch loop.
//        String input;
//        BufferedReader inputLine;
//        try {
//            inputLine = new BufferedReader(new InputStreamReader(System.in));
//            System.out.println("Communicate data to Wiki? (y/n)");
//            input = inputLine.readLine();
//            //input = "y";
//            if (input.compareToIgnoreCase("y") == 0){
//                uploadToWiki = true;
//            }
//            if (uploadToWiki){
//                System.out.println("Perform a Dry run only? (y/n)");
//                input = inputLine.readLine();
//                //input = "y";
//                if (input.compareToIgnoreCase("n") == 0){
//                    dryRunOnly = false;
//                }
//            }
//        } catch (IOException ex) {
//            System.out.println("Unrecoverable Error. aborting bot.");
//            this.cleanUp();
//            ex.printStackTrace();
//            System.exit(1);
//        }

        if (dryRunOnly) {
            glblBotLog.addEntry("SYSTEM", "WIKI", "Performing a dry run only.", PBBLogging.logs.SY);

        } else {
            glblBotLog.addEntry("SYSTEM", "WIKI", "Performing a full run.", PBBLogging.logs.SY);

        }

        if (uploadToWiki) {
            //if (glblWikiCom == null){
            glblWikiCom = new PBBWikiCom(glblConfiguration.getWikiUsername(), glblConfiguration.getWikiPassword(), glblConfiguration.getWikiDomain(), glblBotLog);
            //}
            glblBotLog.addEntry("SYSTEM", "WIKI", "Approval to upload to wiki.", PBBLogging.logs.SY);
            try {

                pageDir = glblWikiCom.getPageDirectory(glblConfiguration.getPageDir());
            } catch (PBBWikiException ex) {
                System.out.println("Large Error while building Wiki Directory.");
                this.cleanUp();
                ex.printStackTrace();
                System.exit(1);
            }

        } else {
            glblBotLog.addEntry("SYSTEM", "WIKI", "Will <b>NOT</b> be communicating to wiki..", PBBLogging.logs.SY);

        }

        if (fileList != null) {
            String[] pages = new String[fileList.length];
            int runLength = fileList.length;
            //runLength = 2; //DEBUG
            Date TimeNow = null;
            Date TimeRun = null;
            double waitTime = 0;

            String directoryEntries = "";
            for (int i = 0; i < runLength; i++) {
                System.out.println("");
                System.out.print("*******Updating Page " + (i + 1) + " of " + fileList.length);

                data = new PBBdata();
                data = glblFileIO.getFileAliases(glblConfiguration.getSimpleAlias(), data);
                data = glblFileIO.decodeSimple(fileList[i], data);
                myCoder = new PBBWikiCoder(data, gts, glblBotLog);
                //String baseText = myCoder.getWikiCodeNew();

                String protein = data.getValue("Symbol", true).trim();
                String page = protein;
                String geneNum = data.getValue("Hs_EntrezGene", true).trim();
                if (protein.compareTo("") == 0) {
                    //we have a bit problem here - this protein has no symbol.
                    //we are just going to skip it.
                    System.out.println("-- Found Protein without Symbol... Skipping...");
                    continue;
                }
                System.out.println(" --(" + protein + "--" + geneNum + ") *******");

                //add in some time remaining estimates here:
                TimeNow = new Date();

                if (TimeRun != null && i > 0) {
                    //Calculate the differences in the times.. convert to seconds.
                    double wait = ((double) TimeNow.getTime() - TimeRun.getTime()) / 1000;
                    //System.out.println("Wait = " + wait);
                    waitTime = (waitTime * (i - 1) + wait) / i;
                    double waitTotal = waitTime * (runLength - i);
                    long waitmins = (long) (waitTotal / 60);
                    long waitsecs = (long) (waitTotal - waitmins * 60);
                    System.out.println("Estimated time remaining: " + waitmins + " minutes, " + waitsecs + " seconds.");
                }
                //set for the next run time.
                TimeRun = TimeNow;


                //XXX This is a little messy - might want to clean it up for next time.
                glblBotLog.addEntry(protein, "INFO", "Beginning work on " + protein + "...", PBBLogging.logs.UP);
                glblBotLog.addEntry("SYSTEM", "INFO", "Beginning work on " + protein + "...", PBBLogging.logs.SY);
                //now get the references...
                //String geneNum = data.getValue("Hs_EntrezGene",true).trim();
                if (geneNum.compareTo("") != 0) {
                    //need to gather up the references..
                    System.out.println("Gathering References for Gene...");
                    myCoder.setCitationData(glblCitation.getCitations(geneNum));
                    System.out.println("Finished Gathering References.");
                }

                //now we need to check on the Expression image data...
                PBBExpression myExp = null;
                Vector<String> expFiles = null;
                try {
                    if (uploadToWiki) {

                        myExp = new PBBExpression(glblWikiCom, glblBotLog, glblConfiguration.getExpressionImages());

                    } else {
                        myExp = new PBBExpression(null, glblBotLog, glblConfiguration.getExpressionImages());

                    }

                    expFiles = myExp.getExpressionFiles(protein, data);



                } catch (Exception ex) {
                    myExp = null;
                    expFiles = null;
                    glblBotLog.addEntry("SYSTEM", "EXPRESSION", "Error Occured while getting Expression Files: " + ex.getMessage(), PBBLogging.logs.SY);
                    System.out.println("Warning: Error while Getting Expression image Files for gene.");

                }

                //Update the expression file list, if there is one to update with...
                if (expFiles != null) {
                    for (int expFilesCount = 0; expFilesCount < expFiles.size(); expFilesCount++) {
                        if (expFilesCount == 0) {
                            data.addData("GeneAtlas_image", expFiles.get(expFilesCount), true);
                        } else {
                            data.addData("GeneAtlas_image", expFiles.get(expFilesCount), false);

                        }
                    }
                }


                //really bad if we have a blank symboled protein
                if (protein.compareTo("") != 0) {

                    if (uploadToWiki) {
                        //Extra PDB image uploading section...
                        PDBImage:
                        {
                            try {
                                if (protein.compareTo("") == 0) {
                                    throw new PBBWikiException("Blank Protein Symbol.");
                                }
                                //Ok, lets look on wikipedia for an image...
                                String pdb = data.getValue("PDB", true).trim();
                                String imageName = "PBB_Protein_" + protein + "_image.jpg";
                                if (glblWikiCom.wikiHasImage("Image:" + imageName)) {
                                    //image already exists, so just break out of here.
                                    data.addData("image", imageName, true);
                                    if (pdb.compareTo("") != 0) {
                                        data.addData("image_source", "[[Protein_Data_Bank|PDB]] rendering based on " + pdb + ".", true);
                                    } else {
                                        data.addData("image_source", "[[Protein_Data_Bank|PDB]] rendering.", true);

                                    }

                                    break PDBImage;
                                }

                                //String pdb = data.getValue("PDB",true).trim();
                                byte[] imageData = null;
                                File parentDir = new File(fileList[i]).getParentFile();
                                File imageFile = null;
                                imageFile = new File(parentDir, imageName);

                                if (pdb.compareTo("") != 0) {
                                    imageData = glblWikiCom.downloadPdbImage(pdb);
                                    if (imageData != null) {

                                        //Found a file on the PDB - so upload it to wikipedia.
                                        //UPLOAD TO WIKIPEDIA.
                                        //throw new PBBWikiException("DEBUG"); //DEBUG - prevents upload.
                                        if (debugUpload) {
                                            glblWikiCom.uploadImage(imageName, imageData, glblWikiCom.pdbImageCopyright);
                                        } else {
                                            System.out.println("Skipped Actual File Upload due to debugging.");
                                        }
                                        glblBotLog.addEntry(protein, "UPLOAD", "Added new Image to wiki: [[Image:" + imageName + "]]", PBBLogging.logs.CR);
                                        //this will include the image in the newly generated pages.
                                        data.addData("image", imageName, true);
                                        data.addData("image_source", "[[Protein_Data_Bank|PDB]] rendering based on " + pdb + ".", true);

                                    }
                                }

                            } catch (PBBWikiException ex) {
                                //if there is any kind of problem, then we will just default to
                                // doing nothing.  Basically we just skip this step.
                                System.out.println("Warning Occured While Uploading Image:" + ex.getMessage());
                            }
                        }//end PDBImage section
                    }


                    try {
                        //The input data has been Parsed.  We are going to output a
                        //basic version first.  Then the actual copy of the page Later.
                        outputWikiFile(fileList[i] + ".pbf", myCoder.getWikiCodeNew());

                        //Everything from here depends on us communicating with wikipedia...

                        if (uploadToWiki) {
                            //glblPageLocations.put(protein, glblWikiCom.getFullURL(pageBase+page));
                            //glblPageTitles.put(protein,pageBase + page);
                            //Hashtable<String,String> pageDir = glblWikiCom.getPageDirectory(glblConfiguration.getPageDir());

                            //First we are going to see if we have an entry for this gene in the page directory
                            //we do this by geneNum ( <geneNum, Page> )..
                            boolean redirect = false;
                            if (geneNum.compareTo("") != 0) {
                                if (pageDir.containsKey(geneNum)) {
                                    //We run a quick check to make sure the redirected page is correct...
                                    String tmpPage = pageDir.get(geneNum);
                                    System.out.println("Found Entry in Page Directory.. Checking Validity.");
                                    String tmpDl = glblWikiCom.downloadPage(pageBase + tmpPage);
                                    //First lets see if there was any data...
                                    if (tmpDl == null) {
                                        //FOund a blank page.  So we are going to create a new one.. 
                                        page = pageDir.get(geneNum);
                                        glblBotLog.addEntry(protein, "WP:REDIRECT", "Protein Redirected to: [[" + pageBase + page + "|" + page + "]]", PBBLogging.logs.RD);
                                        glblBotLog.addEntry(protein, "HTML:REDIRECT", "Protein Redirected to: " + pageBase + page, PBBLogging.logs.RD);
                                        redirect = true;
                                        glblBotLog.glblDryStatus.put(protein, PBBLogging.dryStatus.Redirected);
                                        System.out.println("Page directory gave blank page. Will be creating new page...");
//                                         
                                    //System.out.println("Page directory gave bad page. Continuing with search...");
//                                        glblBotLog.addEntry(protein,"WP:REDIRECT","Page directory gave bad page ([[" +pageBase + tmpPage +"]]).  Continuing with search.. Please correct entry: <" + geneNum + "," + tmpPage + ">." ,PBBLogging.logs.RD);
//                                        glblBotLog.addEntry(protein,"HTML:REDIRECT","Page directory gave bad page (" +pageBase + tmpPage +").  Continuing with search.. Please correct entry: <" + geneNum + "," + tmpPage + ">." ,PBBLogging.logs.RD);
//                                        
                                    } else if (tmpDl.trim().compareTo("") == 0) {
                                        //Found a blank page
                                        page = pageDir.get(geneNum);
                                        glblBotLog.addEntry(protein, "WP:REDIRECT", "Protein Redirected to: [[" + pageBase + page + "|" + page + "]]", PBBLogging.logs.RD);
                                        glblBotLog.addEntry(protein, "HTML:REDIRECT", "Protein Redirected to: " + pageBase + page, PBBLogging.logs.RD);
                                        redirect = true;
                                        glblBotLog.glblDryStatus.put(protein, PBBLogging.dryStatus.Redirected);
                                        System.out.println("Page directory gave blank page. Will be creating new page...");
//                                      
                                    } else if ((tmpDl.indexOf("{{PBB") != -1) && tmpDl.matches("(?s).*PBB\\s*\\|\\s*geneid\\s*=\\s*" + geneNum + "\\s*.*")) {
                                        //looks like we have a good redirect!
                                        page = pageDir.get(geneNum);
                                        glblBotLog.addEntry(protein, "WP:REDIRECT", "Protein Redirected to: [[" + pageBase + page + "|" + page + "]]", PBBLogging.logs.RD);
                                        glblBotLog.addEntry(protein, "HTML:REDIRECT", "Protein Redirected to: " + pageBase + page, PBBLogging.logs.RD);
                                        redirect = true;
                                        glblBotLog.glblDryStatus.put(protein, PBBLogging.dryStatus.Redirected);
                                        System.out.println("Page directory gave good page.");
//                                      
                                    } else {
                                        //Skip - other data
                                        System.out.println("Page directory gave bad page. Continuing with search...");
                                        glblBotLog.addEntry(protein, "WP:REDIRECT", "Page directory gave bad page ([[" + pageBase + tmpPage + "]]).  Continuing with search.. Please correct entry: <" + geneNum + "," + tmpPage + ">.", PBBLogging.logs.RD);
                                        glblBotLog.addEntry(protein, "HTML:REDIRECT", "Page directory gave bad page (" + pageBase + tmpPage + ").  Continuing with search.. Please correct entry: <" + geneNum + "," + tmpPage + ">.", PBBLogging.logs.RD);
                                    }

                                }
                            }
                            glblPageLocations.put(protein, glblWikiCom.getFullURL(pageBase + page));
                            glblPageTitles.put(protein, pageBase + page);


                            PAGE_SEARCH:
                            {
                                //We are going to put some faith in the operator that the redirected page is the correct page.
                                if (redirect == false) {
                                    //boolean acceptablePage = false;
                                    int oldProteinBox = -1;

                                    //So it becomes necessary to do a search of wikipedia to see if we can find
                                    //the proper web page that contains that data... or at least make sure that
                                    //we are not creating a page that already exists under a different name

                                    Vector<String> searchTerms = new Vector<String>();
                                    //Need to cut down the name so that text after a parenthesis or comma is
                                    //removed
                                    String tmpName = data.getValue("Name", true);

                                    String[] tmpNameTokens = tmpName.split("\\s*\\(.*\\)\\s*");
                                    tmpName = "";
                                    for (int tnt = 0; tnt < tmpNameTokens.length; tnt++) {
                                        tmpName += tmpNameTokens[tnt] + " ";
                                    }
                                    tmpName = tmpName.trim();
                                    int index = -1;
                                    if ((index = tmpName.indexOf(", ")) != -1) {
                                        tmpName = tmpName.substring(0, index);
                                    }

                                    searchTerms.add(this.toWikiStandard(tmpName)); //.add(data.getValue("Name",true));
                                    searchTerms.add(this.toWikiStandard(data.getValue("Symbol", true)));
                                    String[] altSymbols = data.getValueList("AltSymbols");
                                    for (int v = 0; v < altSymbols.length; v++) {
                                        if (altSymbols[v].indexOf("/") == -1 && altSymbols[v].indexOf("?") == -1) {
                                            //no repeats, please.
                                            if (!searchTerms.contains(this.toWikiStandard(altSymbols[v]))) {
                                                searchTerms.add(this.toWikiStandard(altSymbols[v]));
                                            }
                                            if (!searchTerms.contains(this.toWikiStandard(this.toFormalFormat(altSymbols[v])))) {
                                                searchTerms.add(this.toWikiStandard(this.toFormalFormat(altSymbols[v])));
                                            }
                                        }
                                    }
                                    //Gonna hold the indexes of potential pages.
                                    Vector<Integer> foundPage = new Vector<Integer>();
                                    Vector<Integer> otherPage = new Vector<Integer>();
                                    //Change in behavior to only supplying a page name *IF and ONLY IF* we find exactly one page
                                    //that has a control box in it.


                                    //Experiment a Success - no longer need the experiment
                                    //if (this.EXPERIMENT){
                                    if (matchGridPageData.compareTo("") == 0) {
                                        matchGridPageData = "==Page Match Grid==" + lineSep;
                                    }
                                    //String pageSearchData = "* " + protein + ": ";
                                    String pageSearchData = "";
                                    //This is to override the matchGridPageData and pageSearchData vars.
                                    //String pageTargetData = "";
                                    System.out.println("Looking for page - checking " + searchTerms.size() + " potential terms.");
                                    System.out.flush();
                                    for (int v = 0; v < searchTerms.size(); v++) {
                                        System.out.println("Checking " + (v + 1) + " of " + searchTerms.size() + " - " + searchTerms.get(v));
                                        if (searchTerms.get(v).trim().compareTo("") == 0) {
                                            //Blank info - just skip it
                                            continue;
                                        }
                                        String tmpDl = glblWikiCom.downloadPage(pageBase + searchTerms.get(v));
                                        //First lets see if there was any data...
                                        if (tmpDl == null) {
                                            //No Data
                                            pageSearchData += "[[" + pageBase + searchTerms.get(v) + "]] (No Data); ";
                                            continue;
                                        }
                                        if (tmpDl.compareTo("") == 0) {
                                            //No data
                                            pageSearchData += "[[" + pageBase + searchTerms.get(v) + "]] (No Data); ";
                                            continue;
                                        }
                                        if (tmpDl.indexOf("{{hndis}}") != -1) {
                                            //Human Name disambiguity page - we are not going to even count this one.
                                            pageSearchData += "[[" + pageBase + searchTerms.get(v) + "]] (Human Name DisAmbig - not counted);  ";

                                            continue;
                                        }

                                        //Lets see if this is a DisAmbiguity page...
                                        if (tmpDl.toUpperCase().indexOf("{{DISAMBIG}}") != -1) {
                                            //yep, we have a disambiguity page - mark it and move on.
                                            pageSearchData += "[[" + pageBase + searchTerms.get(v) + "]] (DisAmbig);  ";
                                            otherPage.add(new Integer(v));
                                            continue;
                                        }
                                        if (tmpDl.toUpperCase().indexOf("#REDIRECT") != -1) {
                                            //Seems we have a page redirection
                                            //Gonna pull out the target..
                                            int tmpIndex = tmpDl.toUpperCase().indexOf("#REDIRECT");
                                            String tmpRStr = tmpDl.substring(tmpIndex + "#REDIRECT".length()).trim();
                                            if (tmpRStr.indexOf("]]") != -1) {
                                                //just want the first link present..
                                                tmpRStr = tmpRStr.substring(tmpRStr.indexOf("[[") + 2, tmpRStr.indexOf("]]")).trim();
                                            }
                                            pageSearchData += "[[" + pageBase + searchTerms.get(v) + "]] (Redirect -> " + tmpRStr + ");  ";
                                            //We also need to follow redirects - so we are going to add the redirected page to the search
                                            tmpRStr = this.toWikiStandard(tmpRStr);
                                            if (!searchTerms.contains(tmpRStr)) {
                                                searchTerms.add(tmpRStr);
                                            }
                                            //Since we put the redirect page on the queue, there is really no need to count this page

                                            //otherPage.add(new Integer(v));
                                            continue;
                                        }


                                        //Now check for Bot data..
                                        //PBBbotCodes tmpCodes = new PBBbotCodes(tmpDl);
                                        if (tmpDl.indexOf("{{PBB_Controls") != -1) {
                                            //if (tmpCodes.badFormat == false){
                                            //Think we found our actual protein page
                                            // going to verify by checking the entrez gene number
                                            if (tmpDl.matches("(?s).*Hs_EntrezGene\\s*=\\s*" + geneNum + ".*")) {
                                                pageSearchData += "[[" + pageBase + searchTerms.get(v) + "]] (Good Codes + Entrez Match); ";
                                                foundPage.add(new Integer(v));
                                                otherPage.add(new Integer(v));

                                            } else {
                                                //Basically we have found a data page for a different protein. We just skip it.
                                                pageSearchData += "[[" + pageBase + searchTerms.get(v) + "]] (Codes Found, but no match[skip]); ";
                                                if (page.compareTo(searchTerms.get(v)) == 0) {
                                                    //do have a conflict with the page name though, so we do need to put
                                                    // an "other page" down.
                                                    otherPage.add(new Integer(v));
                                                }
                                            }
                                            //page = searchTerms.get(v);
                                            //foundPage.add(new Boolean("true"));
                                            //acceptablePage = true;
                                            //foundPage.add(new Integer(v));
                                            //otherPage.add(new Integer(v));
                                            //glblBotLog.glblDryStatus.put(protein,PBBLogging.dryStatus.Found_Match);
                                            continue;
                                        //break PAGE_SEARCH;
                                        }
                                        //check for some other kind of protein template..
                                        if (tmpDl.toUpperCase().indexOf("{{PROTEIN") != -1) {
                                            //found an 'old' protein box.
                                            pageSearchData += "[[" + pageBase + searchTerms.get(v) + "]] (Protein Template);  ";
                                            otherPage.add(new Integer(v));
                                            oldProteinBox = v;
                                            //acceptablePage = true;
                                            continue;
                                        }

                                        //Basically we found a normal page - might be the correct one!
                                        pageSearchData += "[[" + pageBase + searchTerms.get(v) + "]] (Unknown Data);  ";
                                        otherPage.add(new Integer(v));
                                    //acceptablePage = true;
                                    }
                                    glblBotLog.glblProteinTargetPages.put(protein, pageSearchData);
                                    matchGridPageData += "* " + protein + ": " + pageSearchData + lineSep;
//                                        experimentalPageData += "* [[" + data.getValue("Name",true) + "]], ";
//                                        experimentalPageData += "[[" + data.getValue("Symbol",true) + "]]";
//                                        String[] alts = data.getValueList("AltSymbols");
//                                        //experimentalPageData += "[[" + data.getValues("AltSymbols", "]] [[ ",true) + "]]" + lineSep + lineSep;
//                                        for (int v = 0; v < alts.length; v ++){
//                                            if (alts[v].indexOf("/") == -1 && alts[v].indexOf("?") == -1){
//                                                experimentalPageData += ", [[" + alts[v] + "]], [[" + this.toFormalFormat(alts[v]) + "]]";
//                                            }
//                                        }
//                                        experimentalPageData += lineSep;
                                    //}//End if Experiment

                                    //foundPage serves 3 functions: the first is to determine if we found a good page.
                                    //the second is that if its size is 0, then we need to make a new page..
                                    //the Third is that if its size is 1 and false, then we need to attempt to update that page
                                    //if the size is greater than 1 and the total is false, then we have ambiguous pages that we need
                                    //to report.

                                    //boolean foundTotal = false;
                                    //for (int j = 0; j < foundPage.size(); j++) {
                                    //    foundTotal = foundTotal || foundPage.get(j).booleanValue();
                                    //}
                                    //If any of the boolean values were true, then foundTotal will be true.
                                    //Of course this won't really matter because we broke this label if any of them were true...





                                    if (foundPage.size() == 0) {
                                        //No matches at all, probably need to create a new protein then.
                                        glblBotLog.glblDryStatus.put(protein, PBBLogging.dryStatus.No_Matches);
                                        //glblBotLog.addEntry(protein,"SEARCH","Multiple Conflicting Pages. Need Resolution.",PBBLogging.logs.SK);
                                        if (otherPage.size() > 0) {
                                            //did find other possible pages, so we won't create a new page.
                                            glblBotLog.addEntry(protein, "WP:AMBIGUITY", "Did not locate an acceptable page to update.", PBBLogging.logs.SK);
                                            if (oldProteinBox > -1) {
                                                //we found a very possible match, but we won't act on it.
                                                glblBotLog.glblDryStatus.put(protein, PBBLogging.dryStatus.High_Match);
                                                glblBotLog.glblProteinStatus.put(protein, PBBLogging.pStatus.Skip_amb_page);

                                            } else {
                                                //no good match at all.
                                                glblBotLog.glblDryStatus.put(protein, PBBLogging.dryStatus.No_Matches);

                                                glblBotLog.glblProteinStatus.put(protein, PBBLogging.pStatus.Skip_amb_page);
                                            }
                                            glblBotLog.addEntry(protein, "BLOCK", StringEscapeUtils.escapeHtml(myCoder.getWikiCodeNew()), PBBLogging.logs.SK);

                                            System.out.println(protein + ": Protein Status-" + glblBotLog.glblProteinStatus.get(protein).text());
                                            continue; //basically just skip this protein.
                                        } else {
                                            glblBotLog.addEntry(protein, "WP:CREATE", "Found no pages, creating new page.", PBBLogging.logs.CR);
                                            glblBotLog.glblProteinStatus.put(protein, PBBLogging.pStatus.created);

                                        }
                                    }
                                    if (foundPage.size() > 1) {
                                        String tmpWp = "";
                                        String tmpHtml = "";
                                        for (int j = 0; j < foundPage.size(); j++) {
                                            tmpWp = tmpWp + "[[" + searchTerms.get(foundPage.get(j).intValue()) + "]] ";
                                            tmpHtml = tmpHtml + searchTerms.get(foundPage.get(j).intValue()) + " ";
                                        }
                                        glblBotLog.glblDryStatus.put(protein, PBBLogging.dryStatus.Page_Conflict);
                                        glblBotLog.addEntry(protein, "SEARCH", "Multiple Conflicting Pages. Need Resolution.", PBBLogging.logs.SK);
                                        glblBotLog.addEntry(protein, "HTML:AMBIGUITY", "More than one potential page found for updating, " + tmpHtml, PBBLogging.logs.SK);
                                        glblBotLog.addEntry(protein, "WP:AMBIGUITY", "More than one potential page found for updating, " + tmpWp, PBBLogging.logs.SK);
                                        glblBotLog.addEntry(protein, "BLOCK", StringEscapeUtils.escapeHtml(myCoder.getWikiCodeNew()), PBBLogging.logs.SK);

                                        glblBotLog.glblProteinStatus.put(protein, PBBLogging.pStatus.Skip_amb_page);
                                        System.out.println(protein + ": Protein Status-" + glblBotLog.glblProteinStatus.get(protein).text());

                                        continue; //basically just skip this protein.
                                    }
                                    if (foundPage.size() == 1) {
                                        //We did find the page.
                                        page = searchTerms.get(foundPage.get(0).intValue());
                                        glblBotLog.addEntry(protein, "SEARCH REDIRECT", "Control Box Found: " + page, PBBLogging.logs.UP);
                                        glblBotLog.glblDryStatus.put(protein, PBBLogging.dryStatus.Redirected);

                                    }
                                // If we made it here, we may be making a new page, or updating a pre-existing control box page.


                                } //end if no redirect
                            }// END PAGE_SEARCH

                            pages[i] = pageBase + page;
                            String updatedPage = "";

                            if (!dryRunOnly) {
                                System.out.println("Found page location. Attempting Update...");
                                if (redirect == false) {
                                    //we didn't have an entry in the protein directory... time to add one.
                                    directoryEntries += "<" + geneNum + "," + page + "> " + lineSep;
                                }
                                updatedPage = this.updateWikiPage(protein, page, data, fileList[i], myCoder);
                            } else {
                                System.out.println("Skipping update due to Dry Run.");
                            }
                            outputWikiFile(fileList[i] + ".pbf.wiki", updatedPage);
                            if (glblBotLog.glblProteinStatus.containsKey(protein)) {
                                System.out.println(protein + ": Protein Status - " + glblBotLog.glblProteinStatus.get(protein).text());
                            } else {
                                System.out.println(protein + ": Protein Status - Unresolved.");
                                glblBotLog.glblProteinStatus.put(protein, PBBLogging.pStatus.unresolved);

                            }
                        }//end if Wiki

                    } catch (PBBWikiException ex) {
                        System.out.println("Error while Processing: " + fileList[i]);
                        System.out.println(ex.getMessage());
                        System.out.println("Continue with next Protein?");
                        glblBotLog.addEntry("SYSTEM", "PROC_ERROR:", "Error while Processing " + page, PBBLogging.logs.SY);

                        glblBotLog.addEntry(protein, "PROC_ERROR:", "Error while Processing " + page, PBBLogging.logs.ER);
                        glblBotLog.glblProteinStatus.put(protein, PBBLogging.pStatus.Skip_error);

                        try {
                            BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
                            System.out.flush();
                            String inStr = in.readLine();
                            if (inStr.compareToIgnoreCase("y") != 0) {
                                System.out.println("Processing Aborted. Last Successful Protein: " + fileList[i]);

                                this.cleanUp();
                                System.exit(1);
                            }


                        } catch (IOException iex) {
                            System.out.println("Unrecoverable IO Error. Aborting...");
                            this.cleanUp();
                            System.exit(1);

                        }

                    }
                } else {
                    glblBotLog.addEntry("SYSTEM", "BLANK:", "Protein Name/Symbol is blank.  Skipping " + fileList[i], PBBLogging.logs.SY);

                    System.out.println("Protein Name/ Symbol is blank. Skipping " + fileList[i]);
                    pages[i] = "DATA ERROR - See Log File.";
                }


            //***LOOP!***
            }//end FOR
            //Time to output the log files..

            //First need to append the log location to the Main log...

            //Time to do a little bookkeeping for the pages we have already built.
            if (uploadToWiki) {
                System.out.println("Processing Log file for Wikipedia...");
                glblBotLog.addEntry("SYSTEM", "LOG", "Outputting log file to Wikipedia...", PBBLogging.logs.SY);
                String mainLog = glblConfiguration.getWikiMainLog();
                //String title = "User:JonSDSUGrad/Sandbox/Auto_Gen2";

                //The actual log title is now retrieved above..
                String title = glblRunLogPage; //glblConfiguration.getWikiLogRun();
                String text = "";
                String old = "";
                try {

                    //Going to try and do a little appending...

                    //Now we have the link to the log file - we overwrite the current log information there.
                    text = "==Log file for [[User:ProteinBoxBot|Protein Box Bot]]==" + lineSep;
                    text = text + "''Log page index: [[" + mainLog + "]]''<br>" + lineSep;
                    if (dryRunOnly) {
                        text = text + "<big>''' This is a Dry run only, no pages were created, updated, or modified in any way.'''</big>" + lineSep;
                    }

                    //EXPERIMENTAL (Success! - no longer an experiment..
                    //if (this.EXPERIMENT){
                    //text = text + this.matchGridPageData + lineSep;

                    //}
                    //text = text + glblBotLog.getWikiLogDryStatus() + lineSep;
                    //text = text + glblBotLog.getWikiLogProteinStatus(glblPageTitles) + lineSep;
                    text = text + glblBotLog.getWikiQuickLog(glblPageTitles) + lineSep;
                    text = text + glblBotLog.getWikiLogStatusGrid(glblPageTitles) + lineSep;
                    //text = text + glblBotLog.getWikiLogCondensed(glblPageTitles) + lineSep;
                    text = text + glblBotLog.getWikiLog(glblPageTitles) + lineSep;
                    //if(debugUpload){
                    try {
                        boolean success = false;
                        while (!success) {
                            old = glblWikiCom.downloadPage(title).trim();
                            if (old.compareTo("") == 0) {
                                //Looks like we are uploading a new page...
                                System.out.println("Uploading Log File...");
                                glblWikiCom.uploadPage(title, text, "Automatic update.");
                                success = true;
                            //text = "==Links to [[User:ProteinBoxBot|Protein Box Bot]] Logs==" + lineSep;
                            } else {

                                BufferedReader inputLine = new BufferedReader(new InputStreamReader(System.in));
                                System.out.println("Log page: " + title + " already exists, overwrite? (y/n)");
                                String input = inputLine.readLine();
                                //input = "y";
                                if (input.compareToIgnoreCase("y") == 0) {
                                    System.out.println("Uploading Log File...");
                                    glblWikiCom.uploadPage(title, text, "Automatic update.");
                                    success = true;
                                } else {
                                    System.out.println("Please enter a new page title, blank to quit: (previous = " + title + ")");
                                    input = inputLine.readLine();
                                    if (input.compareTo("") == 0) {
                                        throw new IOException("Cannot output log file.");
                                    } else {
                                        title = input;
                                    }
                                }


                            }
                        }
                    } catch (IOException ex) {
                        System.out.println("Unrecoverable Error. aborting bot.");
                        System.out.println("ERROR: " + ex.getMessage());
                        this.cleanUp();
                        //ex.printStackTrace();
                        System.exit(1);
                    }
                    //Now to possibly update the protein directory...
                    if (directoryEntries.compareTo("") != 0) {
                        //we have a few new entries to add...
                        System.out.println("Updating Protein Page Directory...");
                        old = glblWikiCom.downloadPage(glblConfiguration.getPageDir()).trim();
                        text = old + lineSep + directoryEntries;
                        glblWikiCom.uploadPage(glblConfiguration.getPageDir(), text, "PBB Auto-update.");
                    }

                    //now for the Log Index.
                    old = glblWikiCom.downloadPage(mainLog).trim();
                    if (old.compareTo("") == 0) {
                        //Want to add a header if the page is blank...
                        text = "==Links to [[User:ProteinBoxBot|Protein Box Bot]] Logs==" + lineSep;
                    } else {
                        text = old + lineSep;
                    }
                    text = text + "# Log File: [[" + title + "]] - Date: ~~~~~" + lineSep;
                    System.out.println("Appending log file to Main Log...");
                    glblWikiCom.uploadPage(mainLog, text, "Auto-appended next log.");

                    //glblWikiCom.uploadPage(title,text,"Automatic update.");
                    System.out.println("Finished Uploading Log.");
                //}else {
                //    System.out.println("Skipped Actual Upload due to debugging");
                //}
                } catch (PBBWikiException ex) {
                    glblBotLog.addEntry("SYSTEM", "UPLOAD ERROR:", "Problem uploading Log Files list", PBBLogging.logs.SY);
                    System.out.println("Error While Uploading Page List:");
                    System.out.println(ex.getMessage());
                }
            }
        }

        return;
    }


//proceedure added to break up the main processing - used to update/create a page on wikipedia.
//returns the text for the updated page.
    private String updateWikiPage(String protein, String page, PBBdata data, String fileName, PBBWikiCoder myCoder) throws PBBWikiException {

        String editComment = "Automatic Update";

        //check on the global wiki class - can't do anything with it null..
        if (glblWikiCom == null) {
            glblWikiCom = new PBBWikiCom(glblConfiguration.getWikiUsername(), glblConfiguration.getWikiPassword(), glblConfiguration.getWikiDomain(), glblBotLog);
        }
        //page = pageBase + page;
        //pages[i] = (pageBase + page);

        String pdb = data.getValue("PDB", true).trim();

        //find out what we have to start with...
        String existingText = glblWikiCom.downloadPage(pageBase + page);
        String newText = "";
        if (existingText == null) {
            existingText = "";
        }
        //Get info about the bot commands...
        PBBbotCodes botCodes = new PBBbotCodes(existingText);


        //Gonna try and get an image filename if that doesn't work
        //then we'll look in the local system, then if that
        //doesn't work we'll try and download an image from the PDB.
        String imageVal = "";
        String imageCaption = "";

        //Now we need to see if a protein box exists for this page already, and if it does, we need to extract
        //the image data from it.
        if (botCodes.UpdateBox()) {
            Hashtable<String, String> boxFields = botCodes.getBoxFields(PBBbotCodes.templates.GNF_PROTEIN_BOX);
            if (boxFields.containsKey("image")) {
                imageVal = boxFields.get("image");
            }
            if (boxFields.containsKey("image_source")) {
                imageCaption = boxFields.get("image_source");
            }
        }
        
        //Ok, it has become desirable to ensure that the image actually listed in the protein box does exist,
        //so we'll make the check here..
        
        if (imageVal.compareTo("") != 0) {
            //Make sure the image exists on Wikipedia...
            if (!glblWikiCom.wikiHasImage("Image:" + imageVal)) {
                //Ok we don't have a valid image, so we will blank the line..
                imageVal = "";
                //Also blank the caption - no valid image, then no valid caption
                imageCaption = "";
            }
        }
        
        

        if (imageCaption.compareTo("") == 0) {

            //Quickly set up the image caption being as there is not one..
            //imageCaption = "[[Protein_Data_Bank|PDB]] rendering.";
            if (pdb.compareTo("") != 0) {
                imageCaption = "[[Protein_Data_Bank|PDB]] rendering based on " + pdb + ".";

            }
        }

        if (imageVal.compareTo("") == 0) {
            //Ok, lets look on wikipedia for an image...
            String imageName = "PBB_Protein_" + protein + "_image.jpg";
            if (glblWikiCom.wikiHasImage("Image:" + imageName)) {
                imageVal = imageName;
            }
        }
        //DEBUG - Force a new File.
        //imageVal = "";
        if (imageVal.compareTo("") == 0) {
            //didn't find an image on Wikipedia... lets look locally.
            byte[] imageData = null;

            String imageName = "PBB_Protein_" + protein + "_image.jpg";
            File parentDir = new File(fileName).getParentFile();
            File imageFile = null;
            if (parentDir != null) {
                imageFile = new File(parentDir, imageName);

                if (imageFile.exists()) {
                    if (imageFile.canRead()) {
                        try {

                            //Grab the file - we also need to upload it to wikipedia.

                            imageData = glblFileIO.getFileContentBytes(parentDir.getAbsolutePath() + "/" + imageName);
                        } catch (IOException ex) {
                            imageData = null;
                        }
                        if (imageData != null) {
                            //actually got some data..
                            imageVal = imageName;
                        }
                    }
                }
                //Note that we have recently duplicated this image upload to a much earlier location
                //in the code - we have left the dupliacte here to reduce risk of breakage.

                if (imageVal.compareTo("") == 0) {
                    //No image locally - need to check the pdb...
                    if (pdb.compareTo("") != 0) {
                        imageData = glblWikiCom.downloadPdbImage(pdb);
                        if (imageData != null) {
                            try {
                                //Found a file on the PDB - so we store it locally...

                                glblFileIO.dumpDataToFile(imageFile.getAbsolutePath(), imageData);
                                imageVal = imageName;
                            } catch (IOException ex) {
                                //Something is wrong here - bad data?
                                imageData = null;
                            }
                        }
                    }
                }
                //Now we have to upload the file data to wikipedia, if we found any..
                if (imageVal.compareTo("") != 0 && imageData != null) {
                    try {
                        //UPLOAD TO WIKIPEDIA.
                        //throw new PBBWikiException("DEBUG"); //DEBUG - prevents upload.
                        if (debugUpload) {
                            glblWikiCom.uploadImage(imageName, imageData, glblWikiCom.pdbImageCopyright);
                        } else {
                            System.out.println("Skipped Actual File Upload due to debugging.");
                        }
                        glblBotLog.addEntry(protein, "UPLOAD", "Added new Image to wikiCreated new protein page: <a href=" + glblWikiCom.getFullURL(pageBase + page) + ">" + pageBase + page + "</a>", PBBLogging.logs.CR);
                    } catch (PBBWikiException ex) {
                        //if there is any kind of problem, then we will just default to
                        //no image data available.
                        imageVal = "";
                        imageCaption = "";
                    }
                    //Now make sure the image is there..
                    if (!glblWikiCom.wikiHasImage("Image:" + imageName)) {
                        imageVal = "";
                        imageCaption = "";

                    }
                }
            }
        }
        //after all that, we place the valus in the data if they exist...
        // We also overwrite any previous image data that might have been upload earlier..

        if (imageVal.compareTo("") != 0) {
            //looks like we have image data.
            data.addData("image", imageVal, true);
        }
        if (imageCaption.compareTo("") != 0) {
            //looks like we have image data.
            data.addData("image_source", imageCaption, true);
        }

        System.out.println("Image Name: " + data.getValue("image", true));

        System.out.println("Image Caption: " + data.getValue("image_source", true));

        //gather up a new page..

        if (existingText.trim().compareTo("") == 0) {
            //We have a blank page - we are going to create it!
            String baseText = myCoder.getWikiCodeNew();

            if (debugUpload) {
                glblWikiCom.uploadPage(pageBase + page, baseText, "New Page Created by Protein Box Bot");
            } else {
                System.out.println("Skipped Actual Upload due to debugging");
            }
            glblBotLog.addEntry(protein, "HTML:CREATED", "Created new protein page: <a href=" + glblWikiCom.getFullURL(pageBase + page) + ">" + pageBase + page + "</a>", PBBLogging.logs.CR);
            glblBotLog.addEntry(protein, "WP:CREATED", "Created new protein page: [[" + pageBase + page + "]]", PBBLogging.logs.CR);
            glblBotLog.glblProteinStatus.put(protein, PBBLogging.pStatus.created);
            return baseText;

        }

        if (botCodes.NoBots()) {
            //Looks like someone doesn't want our bot to touch this page.  No problem.  We'll just output the message and the possible code.'

            String baseText = myCoder.getWikiCodeNew();

            glblBotLog.addEntry(protein, "NO BOT", "Encounted 'No Bot Edits' flag for this protein.", PBBLogging.logs.SK);
            glblBotLog.addEntry(protein, "WP:NO JOB", "Location: [[" + pageBase + page + "]]", PBBLogging.logs.SK);
            glblBotLog.addEntry(protein, "HTML:NO JOB", "Location: <a href=" + glblWikiCom.getFullURL(pageBase + page) + ">" + pageBase + page + "</a>", PBBLogging.logs.SK);
            glblBotLog.addEntry(protein, "BLOCK", StringEscapeUtils.escapeHtml(baseText), PBBLogging.logs.SK);
            glblBotLog.glblProteinStatus.put(protein, PBBLogging.pStatus.Skip_no_bots);

            return existingText;
        }
        if (botCodes.badFormat) {
            //we have an error somewhere - we will just display a new page.
            String baseText = myCoder.getWikiCodeNew();
            glblBotLog.addEntry(protein, "BAD FORMAT", "There is a problem with the BOT commands for this protein: [[" + pageBase + page + "]]. Invoking a Mandantory Inspection.", PBBLogging.logs.SK);
            glblBotLog.addEntry(protein, "BLOCK", StringEscapeUtils.escapeHtml(baseText), PBBLogging.logs.SK);
            glblBotLog.glblProteinStatus.put(protein, PBBLogging.pStatus.Skip_tag_error);
            return baseText;
        }
        if ((botCodes.UpdateBox() || botCodes.UpdateSum() || botCodes.UpdateCite()) == false) {

            //No job, nothing to update.
            //might be due to a warning...
            if (botCodes.warning == true) {
                glblBotLog.addEntry(protein, "WARNING", "Warning while Updating: " + botCodes.warningError, PBBLogging.logs.SK);
            }
            String baseText = myCoder.getWikiCodeNew();
            glblBotLog.addEntry(protein, "NO JOB", "Aborted upload due to all updates being turned off. No errors.", PBBLogging.logs.SK);
            glblBotLog.addEntry(protein, "WP:NO JOB", "Location: [[" + pageBase + page + "]]", PBBLogging.logs.SK);
            glblBotLog.addEntry(protein, "HTML:NO JOB", "Location: <a href=" + glblWikiCom.getFullURL(pageBase + page) + ">" + pageBase + page + "</a>", PBBLogging.logs.SK);
            glblBotLog.addEntry(protein, "BLOCK", StringEscapeUtils.escapeHtml(baseText), PBBLogging.logs.SK);

            glblBotLog.glblProteinStatus.put(protein, PBBLogging.pStatus.Skip_no_update);
            return existingText;
        }



        //If we get to here, then we will likely be uploading an update.

        String summarySectionTitle = botCodes.getBoxFields(PBBbotCodes.templates.PBB_SUMMARY).get("section_title");
        if (summarySectionTitle == null) {
            summarySectionTitle = "";
        }

        int pBoxS = botCodes.getBoxBegin(PBBbotCodes.templates.GNF_PROTEIN_BOX);
        int pBoxE = botCodes.getBoxEnd(PBBbotCodes.templates.GNF_PROTEIN_BOX);
        int sBoxS = botCodes.getBoxBegin(PBBbotCodes.templates.PBB_SUMMARY);
        int sBoxE = botCodes.getBoxEnd(PBBbotCodes.templates.PBB_SUMMARY);
        int cBoxS = botCodes.getBoxBegin(PBBbotCodes.templates.PBB_FURTHER_READING);
        int cBoxE = botCodes.getBoxEnd(PBBbotCodes.templates.PBB_FURTHER_READING);
        String pBoxD = myCoder.getWikiCodePB();
        String sBoxD = myCoder.getWikiCodeSum(summarySectionTitle);
        String cBoxD = myCoder.getFurtherReadingCitations();


        if (botCodes.UpdateBox() && botCodes.UpdateSum() && botCodes.UpdateCite()) {
            //need to update all three..

            if (pBoxS < sBoxS && pBoxS < cBoxS) {
                //start with pBox
                newText = existingText.substring(0, pBoxS);
                newText += pBoxD;

                if (sBoxS < cBoxS) {
                    //Summary is second, followed by Citations.
                    newText += existingText.substring(pBoxE, sBoxS);
                    newText += sBoxD;
                    newText += existingText.substring(sBoxE, cBoxS);
                    newText += cBoxD;
                    newText += existingText.substring(cBoxE);

                } else {
                    newText += existingText.substring(pBoxE, cBoxS);
                    newText += cBoxD;
                    newText += existingText.substring(cBoxE, sBoxS);
                    newText += sBoxD;
                    newText += existingText.substring(sBoxE);

                }
            } else if (sBoxS < pBoxS && sBoxS < cBoxS) {
                //start with summary..
                newText = existingText.substring(0, sBoxS);
                newText += sBoxD;

                if (pBoxS < cBoxS) {
                    //protein is second, followed by Citations.
                    newText += existingText.substring(sBoxE, pBoxS);
                    newText += pBoxD;
                    newText += existingText.substring(pBoxE, cBoxS);
                    newText += cBoxD;
                    newText += existingText.substring(cBoxE);

                } else {
                    newText += existingText.substring(sBoxE, cBoxS);
                    newText += cBoxD;
                    newText += existingText.substring(cBoxE, pBoxS);
                    newText += pBoxD;
                    newText += existingText.substring(pBoxE);

                }
            } else if (cBoxS < pBoxS && cBoxS < sBoxS) {
                //start with Citations...
                newText = existingText.substring(0, cBoxS);
                newText += cBoxD;

                if (pBoxS < sBoxS) {
                    //protein is second, followed by summary.
                    newText += existingText.substring(cBoxE, pBoxS);
                    newText += pBoxD;
                    newText += existingText.substring(pBoxE, sBoxS);
                    newText += sBoxD;
                    newText += existingText.substring(sBoxE);

                } else {
                    newText += existingText.substring(cBoxE, sBoxS);
                    newText += sBoxD;
                    newText += existingText.substring(sBoxE, pBoxS);
                    newText += pBoxD;
                    newText += existingText.substring(pBoxE);

                }
            }
            glblBotLog.addEntry(protein, "UPDATE PROTEIN BOX", "Updating Protein Box, No errors.", PBBLogging.logs.UP);
            glblBotLog.addEntry(protein, "UPDATE SUMMARY", "Updating Summary, No Errors.", PBBLogging.logs.UP);
            glblBotLog.addEntry(protein, "UPDATE CITATIONS", "Updating Citations, No Errors.", PBBLogging.logs.UP);

        } else if (botCodes.UpdateBox() && botCodes.UpdateSum()) {
            //only update 2, the box and the summaries.
            if (pBoxS < sBoxS) {
                //protein is first, followed by summary.
                newText += existingText.substring(0, pBoxS);
                newText += pBoxD;
                newText += existingText.substring(pBoxE, sBoxS);
                newText += sBoxD;
                newText += existingText.substring(sBoxE);

            } else {
                newText += existingText.substring(0, sBoxS);
                newText += sBoxD;
                newText += existingText.substring(sBoxE, pBoxS);
                newText += pBoxD;
                newText += existingText.substring(pBoxE);

            }
            glblBotLog.addEntry(protein, "UPDATE PROTEIN BOX", "Updating Protein Box, No errors.", PBBLogging.logs.UP);
            glblBotLog.addEntry(protein, "UPDATE SUMMARY", "Updating Summary, No Errors.", PBBLogging.logs.UP);
            glblBotLog.addEntry(protein, "SKIP CITATIONS", "Skipping Citations, No Errors.", PBBLogging.logs.UP);

        } else if (botCodes.UpdateBox() && botCodes.UpdateCite()) {
            //only update 2, the box and the Citations.
            if (pBoxS < cBoxS) {
                //protein is first, followed by Citations.
                newText += existingText.substring(0, pBoxS);
                newText += pBoxD;
                newText += existingText.substring(pBoxE, cBoxS);
                newText += cBoxD;
                newText += existingText.substring(cBoxE);

            } else {
                newText += existingText.substring(0, cBoxS);
                newText += cBoxD;
                newText += existingText.substring(cBoxE, pBoxS);
                newText += pBoxD;
                newText += existingText.substring(pBoxE);

            }
            glblBotLog.addEntry(protein, "UPDATE PROTEIN BOX", "Updating Protein Box, No errors.", PBBLogging.logs.UP);
            glblBotLog.addEntry(protein, "SKIP SUMMARY", "SKIPPING Summary, No Errors.", PBBLogging.logs.UP);
            glblBotLog.addEntry(protein, "UPDATE CITATIONS", "Updating Citations, No Errors.", PBBLogging.logs.UP);

        } else if (botCodes.UpdateCite() && botCodes.UpdateSum()) {
            //only update 2, the Citations and the summaries.
            if (cBoxS < sBoxS) {
                //protein is first, followed by summary.
                newText += existingText.substring(0, cBoxS);
                newText += cBoxD;
                newText += existingText.substring(cBoxE, sBoxS);
                newText += sBoxD;
                newText += existingText.substring(sBoxE);

            } else {
                newText += existingText.substring(0, sBoxS);
                newText += sBoxD;
                newText += existingText.substring(sBoxE, cBoxS);
                newText += cBoxD;
                newText += existingText.substring(cBoxE);

            }
            glblBotLog.addEntry(protein, "SKIP PROTEIN BOX", "Skipping Protein Box, No errors.", PBBLogging.logs.UP);
            glblBotLog.addEntry(protein, "UPDATE SUMMARY", "Updating Summary, No Errors.", PBBLogging.logs.UP);
            glblBotLog.addEntry(protein, "UPDATE CITATIONS", "Updating Citations, No Errors.", PBBLogging.logs.UP);

        } else if (botCodes.UpdateBox()) {
            //only update protein
            newText += existingText.substring(0, pBoxS);
            newText += pBoxD;
            newText += existingText.substring(pBoxE);
            glblBotLog.addEntry(protein, "UPDATE PROTEIN BOX", "Updating Protein Box, No errors.", PBBLogging.logs.UP);
            glblBotLog.addEntry(protein, "SKIP SUMMARY", "Skipping Summary, No Errors.", PBBLogging.logs.UP);
            glblBotLog.addEntry(protein, "SKIP CITATIONS", "Skipping Citations, No Errors.", PBBLogging.logs.UP);


        } else if (botCodes.UpdateSum()) {
            //only update Summary
            newText += existingText.substring(0, sBoxS);
            newText += sBoxD;
            newText += existingText.substring(sBoxE);
            glblBotLog.addEntry(protein, "SKIP PROTEIN BOX", "Skipping Protein Box, No errors.", PBBLogging.logs.UP);
            glblBotLog.addEntry(protein, "UPDATE SUMMARY", "Updating Summary, No Errors.", PBBLogging.logs.UP);
            glblBotLog.addEntry(protein, "SKIP CITATIONS", "Skipping Citations, No Errors.", PBBLogging.logs.UP);


        } else if (botCodes.UpdateCite()) {
            //only update Citations
            newText += existingText.substring(0, cBoxS);
            newText += cBoxD;
            newText += existingText.substring(cBoxE);
            glblBotLog.addEntry(protein, "SKIP PROTEIN BOX", "Skipping Protein Box, No errors.", PBBLogging.logs.UP);
            glblBotLog.addEntry(protein, "SKIP SUMMARY", "Skipping Summary, No Errors.", PBBLogging.logs.UP);
            glblBotLog.addEntry(protein, "UPDATE CITATIONS", "Updating Citations, No Errors.", PBBLogging.logs.UP);

        } else {
        //Update nothing...
        //already accounted for this scenario above.
        }

        //now check to see if we need an inspection..
        if (botCodes.ReqsManInspect()) {
            glblBotLog.addEntry(protein, "WP:INSPECTION", "Manual Inspection Required for this protein: [[" + pageBase + page + "]]", PBBLogging.logs.SK);
            glblBotLog.addEntry(protein, "HTML:INSPECTION", "Manual Inspection Required for this protein: <a href=" + glblWikiCom.getFullURL(pageBase + page) + ">" + pageBase + page + "</a>", PBBLogging.logs.SK);
            glblBotLog.addEntry(protein, "BLOCK", StringEscapeUtils.escapeHtml(newText), PBBLogging.logs.SK);
            glblBotLog.glblProteinStatus.put(protein, PBBLogging.pStatus.Skip_manual_insp);
            return newText;
        //outputWikiFile(fileList[i] + ".pbf.wiki", newText);
        //continue; //abort upload and move to next protein.
        }

        //Adjust for editcomment
        editComment = "PBB Automatic Update: ";
        if (botCodes.UpdateBox()) {
            editComment += "Updated Protein Box, ";
        } else {
            editComment += "Skipped Protein Box, ";
        }

        if (botCodes.UpdateSum()) {
            editComment += "Updated Summary, ";
        } else {
            editComment += "Skipped Summary, ";
        }

        if (botCodes.UpdateCite()) {
            editComment += " and Updated Citations.";
        } else {
            editComment += "and Skipped Citations.";
        }

        //If we get here then that means we are updating the page with new data.
        if (debugUpload) {
            glblWikiCom.uploadPage(pageBase + page, newText, editComment);
        } else {
            System.out.println("Skipped Actual Upload due to debugging");
        }

        //Now we need to check and see if we had a warning...
        if (botCodes.warning == true) {
            //we had a warning - need to report it and display a block in the log, but we still update the page.
            glblBotLog.addEntry(protein, "UPDATED", "Warning while updating page: " + botCodes.warningError, PBBLogging.logs.UP);
            String baseText = myCoder.getWikiCodeNew();
            glblBotLog.addEntry(protein, "HTML:UPDATED", "Updated protein page (with warnings): <a href=" + glblWikiCom.getFullURL(pageBase + page) + ">" + pageBase + page + "</a>", PBBLogging.logs.UP);
            glblBotLog.addEntry(protein, "WP:UPDATED", "Updated protein page (with warnings): [[" + pageBase + page + "]]", PBBLogging.logs.UP);
            glblBotLog.addEntry(protein, "BLOCK", StringEscapeUtils.escapeHtml(baseText), PBBLogging.logs.UP);

            glblBotLog.glblProteinStatus.put(protein, PBBLogging.pStatus.updated_warning);

        } else {

            glblBotLog.addEntry(protein, "HTML:UPDATED", "Updated protein page: <a href=" + glblWikiCom.getFullURL(pageBase + page) + ">" + pageBase + page + "</a>", PBBLogging.logs.UP);
            glblBotLog.addEntry(protein, "WP:UPDATED", "Updated protein page: [[" + pageBase + page + "]]", PBBLogging.logs.UP);
            glblBotLog.glblProteinStatus.put(protein, PBBLogging.pStatus.updated);
        }
        return newText;


    }

    private void outputWikiFile(String filename, String data) {
        if (glblFileIO == null) {
            glblFileIO = new PBBFileIO(glblBotLog);
        }
        try {
            glblFileIO.initOutFile(filename);
            glblFileIO.writeFileln(data);
            glblFileIO.killStreams();
        } catch (IOException ex) {
            glblBotLog.addEntry("SYSTEM", "IO ERROR", "Error while outputting to file: " + filename, PBBLogging.logs.ER);

        //ex.printStackTrace();
        }

    }
   
    //We only have to answer once during the batch job.
    private String gtAnswer = "";

    private Hashtable<String, String> retrieveGoTerms() {
        //we have two options - either we get the go terms from a new go file, or we get the terms
        //from a previously parsed file. First we check to see if the command line option has been set
        //to parse from a new file...


        Hashtable<String, String> data = null;

        if (glblParseGoTerms) {
            return PBBxml.xmlGoParse(glblConfiguration.getGoTerms(), this.glblBotLog);
        }

        //Now we see if we already have a parsed file - that will be much quicker to parse in.

        File pbbIn = new File(glblConfiguration.getGoTerms() + ".pbb");
        if (pbbIn.canRead()) {
            try {
                //String input;
                if (gtAnswer.compareTo("") == 0) {
                    BufferedReader inputLine = new BufferedReader(new InputStreamReader(System.in));
                    System.out.println("Parse Original Go Terms File? (Pre-Parsed File was found.) (y/n)");
                    gtAnswer = inputLine.readLine();
                }
                if (gtAnswer.compareToIgnoreCase("y") == 0) {
                    return PBBxml.xmlGoParse(glblConfiguration.getGoTerms(), this.glblBotLog);
                } else {
                    BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(pbbIn)));
                    String line = "";
                    data = new Hashtable<String, String>();
                    glblBotLog.addEntry("SYSTEM", "GO_TERMS", "Using Pre-Parsed go terms file:" + pbbIn.getPath(), PBBLogging.logs.SY);
                    System.out.println("Using Pre-Parsed go Terms File: " + pbbIn.getPath());
                    //The file keys and values are separated by a single ';'
                    while ((line = br.readLine()) != null) {
                        int index = line.indexOf(";");
                        //System.out.println("Go values: " + line.substring(0,index) + " = " + line.substring(index+1));
                        data.put(line.substring(0, index), line.substring(index + 1));
                    }
                }

            } catch (Exception e) {
                //if there is any exception at all then we will just abort and reparse anyway.
                return PBBxml.xmlGoParse(glblConfiguration.getGoTerms(), this.glblBotLog);
            }
        } else {
            return PBBxml.xmlGoParse(glblConfiguration.getGoTerms(), this.glblBotLog);
        }

        return data;
    }

    public static void printHelp() {
        //prints some help to the command line.
        System.out.println("Protein Box Bot");
        System.out.println("");
        System.out.println("Command line options:");
        System.out.println("-nogui  Program will run without a gui.");
        System.out.println("-h /h h help -help  Display this help information.");
        System.out.println("-f <file>  Sets the location of the configuration file.");
        System.out.println("-parsegt Forces a new parse of the go terms xml file. ");
        //System.out.println("Please note that these options are case-sensitive.");
        System.out.println("");

    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // Handle code start logic here:

        //Two main possibilities, Command-line only, and GUI driven.
        //Regardless we will need a configuration filename pointing to the configuration
        //file for the program (this file contains items such as location of XML files
        //for protein info, and possible server address/instrctions for wikipedia).

        boolean uGui = true;
        boolean abortProgram = false; //We will abort if the command line has trouble.
        String confLocation = "";
        boolean pgt = false;
        if (args.length > 0) {

            for (int i = 0; i < args.length; i++) {

                System.out.println("Argument: " + args[i].toUpperCase());
                if (args[i].toUpperCase().compareTo("-NOGUI") == 0) {
                    //user doesn't want to use the gui.
                    uGui = false;
                } else if (args[i].toUpperCase().compareTo("-F") == 0) {
                    //next argument should be the file location.
                    if (i + 1 >= args.length) {
                        //Got the flag, but no following argument.. Abort!
                        abortProgram = true;
                        printHelp();
                        break;
                    } else {
                        confLocation = args[i + 1];
                        i++; //So we skip past the location on the next loop.
                    }
                } else if (args[i].toUpperCase().compareTo("-PARSEGT") == 0) {
                    //force a parse of the go terms file.
                    pgt = true;

                } else {
                    //Got a strange command - just error with the help screen.
                    printHelp();
                    abortProgram = true;
                    break;
                }
            } //end argument loop

        } else {
            abortProgram = true;
            printHelp();

        }
        if (confLocation.compareTo("") == 0) {
            abortProgram = true;
            printHelp();
        }

        if (abortProgram == false) {
            if (uGui == true) {
                //Choose a L&F
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }


            ProteinBoxBot PBB = new ProteinBoxBot(uGui, confLocation, pgt);

        }

    }

    public static String toWikiStandard(String in) {

        String tmp1;
        String tmp2;

        //first thing is that all underscores become spaces.
        in = in.replace("_", " ");
        in = in.trim();
        if (in.length() < 1) {
            return in;
        }
        //next we need to capitalize the first letter
        if (in.length() == 1) {
            return in.toUpperCase();
        }
        tmp1 = in.substring(0, 1); //first letter
        tmp2 = in.substring(1);//rest of the string
        in = tmp1.toUpperCase() + tmp2;

        //Nothing else to do.. return the value.
        return in;
    }

    /**
     * Returns the string as all lower case, except for the first character.
     * @param in String to be formatted.
     * @return Formatted String
     */
    public static String toFormalFormat(String in) {
        //Returns the string as all lower case except for the first character.
        String out = null;
        char[] chars;
        if (in != null) {
            out = "";
            chars = in.trim().toLowerCase().toCharArray();
            if (chars.length > 0) {
                //out = out.toLowerCase().trim();
                //chars = out.toCharArray();
                chars[0] = Character.toUpperCase(chars[0]);
                out = String.valueOf(chars);
            }
        }
        return out;

    }

}

/*
 *Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!)  The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
 *
 */


